package client;

import static common.Config.SERVICE;

import java.awt.*;
import java.awt.event.*;
import java.rmi.*;
import java.rmi.server.*;
import java.util.*;

import javax.swing.*;

import client.frames.*;
import client.game.ClientGameFrame;
import client.game.Game;
import client.game.GameStateInterface;
import client.game.TicTacToeButton;

import model.*;
import model.exceptions.*;

import common.*;

public class ClientController extends UnicastRemoteObject implements
		ClientInterface {
	private ServerInterface server;
	private ClientMainFrame mainFrame;
	private Game game;
	private String name, IP, info;
	private Message chat, message;
	private boolean newChat, newMessage, newButton, newInfo, newHighUsers;
	private ServerButton button;
	private User[] users;

	public ClientController() throws RemoteException {
		super();
		game = null;
		new MessageThread().start();
		new ChatThread().start();
		new ButtonThread().start();
		new InfoThread().start();
		new HighScoreThread().start();
	}

	public void begin(ClientMainFrame frame) {
		this.mainFrame = frame;

		try {
			IP = inputIP();
			String URL = "rmi://" + IP + "/" + SERVICE;
			server = (ServerInterface) Naming.lookup(URL);
			new LoginFrame(this);

		} catch (Exception e) {
			e.printStackTrace();
			frame.error("Trouble connecting to server");
			JOptionPane.showMessageDialog(null,
					"Connection problem. Restart program!");
			System.exit(1);
		}
	}

	public String inputIP() {
		return JOptionPane.showInputDialog(null, "IP");
	}
	
	public void loginFrameClose() {
		System.exit(1);
	}

	public void loginPressed(String name, String password)
			throws AbstractException {
		try {
			server.login(name, password, this);
			mainFrame.takeAllUsers(getAllUsersFromServer());
			mainFrame.setVisible(true);
			this.name = name;
		} catch (RemoteException e) {
			e.printStackTrace();
			throw new ConnectionException();
		}
	}

	public void registerPressed(String name, String password, String repassword)
			throws AbstractException {
		if (!password.equals(repassword))
			throw new PasswordsDontMatchException();
		try {
			server.registerUser(name, password, this);
			mainFrame.takeAllUsers(getAllUsersFromServer());
			mainFrame.setVisible(true);
			this.name = name;
		} catch (RemoteException e) {
			e.printStackTrace();
			throw new ConnectionException();
		}
	}

	public void startPressed(String opponent) throws AbstractException {
		if (opponent.equals(name))
			throw new WrongUserSelectedException();
		if(game!=null)
			throw new YouAreBusyException();
		try {
			server.startGame(opponent, name);
			game = new Game(this, opponent, true);
		} catch (RemoteException e) {
			e.printStackTrace();
			JOptionPane.showMessageDialog(null, "connection problem");
		} catch (AbstractException e) {
			JOptionPane.showMessageDialog(null, e.getText());
			e.printStackTrace();
		}
	}
	
	public void logoutPressed() {
		try {
			server.logout(name);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		mainFrame.setVisible(false);
		System.exit(0);
	}
	
	public synchronized void highScorePressed() {
		try {
			User[] users= server.getHighScores();
			for(int i=0;i<users.length-1;i++)
				for(int j=i+1;j<users.length;j++)
					if(users[i].getScore()<users[j].getScore()) {
						User aux=users[i].copy();
						users[i]=users[j].copy();
						users[j]=aux;
					}
			this.users=users;
			newHighUsers=true;
			notifyAll();
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void showHighScore() {
		while(!newHighUsers)
			try {
				wait();
			} catch(InterruptedException e) {
			}
		new HighScoreTable(users);
		newHighUsers=false;
	}
	
	public ArrayList<String> getAllUsersFromServer() {
		try {
			return server.getAllUsers();
		} catch (RemoteException e) {
			return new ArrayList<String>();
		}
	}

	public synchronized void userLoggedin(String name) throws RemoteException {
		mainFrame.addUser(name);
	}

	public synchronized void userLoggedout(String name) throws RemoteException {
		mainFrame.removeUser(name);
	}
	
	@Override
	public void serverClosed() throws RemoteException {
		mainFrame.serverClosed();
	}
	
	public void sendChat(String text) {
		try {
			server.takeChat(new Message(name, text));
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
	
	public synchronized void takeChat(Message message) throws RemoteException {
		while(newChat)
			try {
				wait();
			} catch(InterruptedException e) {
			}
		chat=message;
		newChat=true;
		notifyAll();
	}
	
	public synchronized void showChat() {
		while(!newChat)
			try {
				wait();
			} catch(InterruptedException e) {
			}
		newChat=false;
		notifyAll();
		mainFrame.showChat(chat);
	}
	
	public synchronized void startConversation(String name)
			throws RemoteException, AbstractException {
		if (game != null) {
			throw new UserBusyException();
		}
		game = new Game(this, name, false);
	}

	public synchronized void endConversation() throws RemoteException {
		game.userExit();
	}
	
	public void chatFrameClose() {
		try {
			server.stopGame(name);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
		game = null;
	}
	
	public synchronized void sendMessage(String text) {
		try {
			server.takeMessage(new Message(name, text));
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public synchronized void takeMessage(Message message)
			throws RemoteException {
		this.message = message;
		newMessage = true;
		notifyAll();
	}

	public synchronized void showMessage() {
		while (!newMessage)
			try {
				wait();
			} catch (InterruptedException e) {
			}
		newMessage = false;
		game.showMessage(message);
	}

	public synchronized void buttonPressed(ServerButton button) {
		try {
			server.takeButton(button, name);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}

	public synchronized void takeButton(ServerButton button)
			throws RemoteException {
		this.button=button;
		newButton=true;
		notifyAll();
	}
	
	public synchronized void setButton() {
		while(!newButton)
			try {
				wait();
			} catch(InterruptedException e) {
			}
		newButton=false;
		game.setButton(button);
	}

	@Override
	public synchronized void takeInfo(String info) throws RemoteException {
		this.info=info;
		newInfo=true;
		notifyAll();
	}
	
	public synchronized void showInfo() {
		while(!newInfo)
			try {
				wait();
			} catch(InterruptedException e) {
			}
		game.showInfo(info);
		newInfo=false;
	}
	public void restart() {
		try {
			server.restart(name);
		} catch (RemoteException e) {
			e.printStackTrace();
		}
	}
	
	private class ChatThread extends Thread {
		public void run() {
			while(true)
				showChat();
		}
	}
	
	private class MessageThread extends Thread {
		public void run() {
			while(true)
				showMessage();
		}
	}
	private class ButtonThread extends Thread {
		public void run() {
			while(true)
				setButton();
		}
	}
	private class InfoThread extends Thread {
		public void run() {
			while(true)
				showInfo();
		}
	}
	private class HighScoreThread extends Thread {
		public void run() {
			while(true)
				showHighScore();
		}
	}
	@Override
	public void resetButtons(boolean myTurn) throws RemoteException {
		if(game!=null)
			game.resetButtons(myTurn);
	}
}